SwiftData
Table of Contents
Now that I’ve upgraded my computer I can use the latest updates in Swift, SwiftUI and the all-new SwiftData introduced last June. I decided to take an old project and update it to use SwiftData to see how it works.
Back in 2016 I made a companion app for an online role-playing game called Clan Lord that fetches the XML feed from the game server, parses it and displays it in an iOS app, along with some astronomical in-game data. I decided to use this real-world example that includes fetching data from the web to test SwiftData.
A few details about the app #
So before I go into what I changed to use SwiftData, a few word about what the app does. Every 3 minutes, the server for the game posts an XML feed with various data about the game. The app fetches this XML data and then parses into this ClanLordStatus
struct:
|
|
The status
gives the date and time of the last refresh of the XML feed. The news
always points to the latest release notes for server updates:
Clan Lord what’s new, clanners
contains the list of currently playing characters, each with the information detailed below, there are four types of announcements
which are also detailed below, and finally poptrend
is the number of online clanners for each update of the XML for the past few hours, which gives a population trend.
OnlineExile
consist of these parameters:
|
|
With this the app can display this type of information:
And Announcement
has these parameters:
|
|
And the corresponding view within the app:
In addition to these two views, the app also displays various information like the current time, the next sunrise and sunset, etc. Here is that view 1.
Adding SwiftData #
The information from the XML feed is ephemeral. People come and go, so the list of online exiles varies over time and there is nothing to persist. To test SwiftData I needed some data that would be persisted, so I decided to include the ability to add notes about an exile and also to enable notifications (to get notified when they log in the game).
My first thought was to simply use my old networking code to pull the XML and parse it into a ClanLordStatus
struct. I’d have an ephemeral list of OnlineExile
and a persistent list of Exile
which contains the same information2 plus the notification and notes as well as an online
parameter. When I refresh the XML feed I go through the list and update the online status and the app displays exiles that are online.
There are four steps to use SwiftData in an app:
- Add the
@Model
macro to the model class (it replaces@Observable
). - Pass in the
ModelContainer
to the view (best done from the@main
struct so that all your views have access to the model but it’s not necessary). - Add data to the model with
.insert(modelObject)
. - Read the data that you need for the purpose of the view with
@Query
.
So in my project I created the Exile
model which is similar to the OnlineExile
struct but adds the online
, notify
and notes
parameters, and added the @Model
macro like this. <- step 1
|
|
Then I pass a ModelContainer to the ContentView (don’t forget to import SwiftData where needed). <- step 2
|
|
To add data you need to have access to the ModelContext
which represents your model in memory and is managed by SwiftData.
You get access to the ModelContext
like this:
@Environment(\.modelContext) var modelContext
Then you add data simply like this. <- step 3
let newPerson = Exile(name: clanner.name, profession: profession, race: race, gender: clanner.gender, clan: clanner.clan)
modelContext.insert(newPerson)
And then when you need to access the data from the model all you need is to read the data with a query which can be configured with predicates and sorting options. So something like this for example. <- step 4
@Query private var people: [Exile]
...
NavigationStack {
List {
ForEach(people) { person in
NavigationLink(value: person) {
Text(person.name)
}
}
}
...
}
That’s all it takes to use SwiftData! This is what the view for an ’exile’ looks like 3:
Current status #
So far so good! Of course, there’s a lot more to SwiftData than just that. This is only a first order implementation to get things working and it works pretty well as far as displaying who is online and persisting the notes. I’m also quite happy with the way the app looks (both in light and dark mode) and how the astronomical information is presented.
The issues I have is that sometimes the app isn’t very responsive, and more importantly the notifications don’t work. I think the performance issues can be resolved by refactoring the code to use async
tasks so that changes to the model occur more smoothly. For notifications, I think I need to look into ModelActor
, and that’ll be the subject of another post. Stay tuned!
-
IRL
means In Real Life, as opposed toCL
for the Clan Lord gameworld. When it’s 3:30 PM in CL time, it’s currently 11:19 PM in real life. But it won’t be the case tomorrow. Times flows 4 times faster in the game world than in real life, so it’s always out of sync from ours and without tools like this it’s impossible to know the exact in-game time. ↩︎ -
With the exception of the name of the character, most of the other parameters can change over time. People can change clan for example, and even the race and gender may change, because they can choose to be “undisclosed” at first and later on reveal it. And the profession can also change over time. So all these parameters will get updated with the new server data as they are fetched. ↩︎
-
For now this is the information displayed, in the future I might add the avatar for the character. The information is contained in the
picture
parameter which gives an ID andcolors
parameter. This is included as part of the XML information. I just need to figure out how to get the picture from the CL Images file and then how to swap the specific exile’s colors with the placeholders in the image. ↩︎